using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public class ResetReg : Component 
    {

        public NodeVector InputNodes;
        public NodeVector OutputNodes;
        public NodeVector EnableNodes;

        public NodeVector ResetIn;
        public NodeVector ResetTo;

        public ClockComponent ClockIn;

        RegisterComponent oRegisterComponent;
        Mux oMux;

        public ResetReg(Component poParentComponent, string psName, ClockComponent poClockIn, int piInputWidth, bool pbRisingEdge, bool pbEnable, bool pbAsyncReset)
            : base(poParentComponent,psName)
        {

            InputNodes = this.CreateNodeVector("INPUT_I",piInputWidth);
            OutputNodes = this.CreateNodeVector("OUTPUT_I",piInputWidth);

            if (pbAsyncReset)
            {
                ClockIn = new ClockSink(this, "clk_async_reset");
                NodeVector nvCLK = Operators.OR(this,poClockIn.ClockOut, ResetIn);
                ClockIn.ClockIn.Connection = nvCLK;
            }
            else
            {
                ClockIn = poClockIn;
            }


            oRegisterComponent = new RegisterComponent(this, "register", ClockIn, piInputWidth, pbRisingEdge, pbEnable);

            OutputNodes.Connection = oRegisterComponent.OutputNodes;
            
            ResetTo = this.CreateNodeVector("RESET_VALUE_I", piInputWidth);
            ResetIn = this.CreateNodeVector("RESET_I", 1);

            oMux = new Mux(this, "reset_mux", piInputWidth, 2);
            oMux.SelectNodes.Connection = ResetIn;
            oMux.InputNodes[0].Connection = InputNodes;
            oMux.InputNodes[1].Connection = ResetTo;

            oRegisterComponent.InputNodes.Connection = oMux.OutputNodes;


            if (pbEnable)
            {
                EnableNodes = this.CreateNodeVector("ENABLE_I",1);
                NodeVector oEnable = Operators.OR(this,ResetIn, EnableNodes);
                oEnable.Connection = oRegisterComponent.EnableNodes;
            }

        }

        protected override Component TestBuildStructure(Component poTopComponent, string psTestName)
        {
            NodeVector nvDataIn = poTopComponent.CreateNodeVector("data_i", 3, NodeFlowType.Source);
            NodeVector nvReset = poTopComponent.CreateNodeVector("reset", 1, NodeFlowType.Source);
            
            TopLevelComponent oTop = (TopLevelComponent)poTopComponent;

            oTop.AddMainClock(1);

            ResetReg oResetReg = new ResetReg(oTop, "test_reg", oTop.MainClock, 3, true, false, false);
            oResetReg.ResetIn.Connection = nvReset;
            oResetReg.ResetTo.Connection = Operators.CONST(oTop,5,3);
            oResetReg.InputNodes.Connection = nvDataIn;

            Utility.SinkExtraBits(oTop, oResetReg.OutputNodes);

            return oResetReg;
        }

        protected override string TestComponent(Clock poClock, Component poTestComponent, string psTestName)
        {
            poTestComponent.SimulationStabilizeComponent();

            
            ResetReg oReg = (ResetReg)poTestComponent;

            NodeVector nvDataIn = poTestComponent.ParentComponent.GetNodeVector(".data_i");
            NodeVector reset = poTestComponent.ParentComponent.GetNodeVector(".reset");

            reset.NodeVectorAsString = "1";
            nvDataIn.NodeVectorAsString = "111";
            poClock.DoClock(4.0);
            if (oReg.OutputNodes.ToString() != "101")
                return "fail reset test";

            reset.NodeVectorAsString = "0";
            nvDataIn.NodeVectorAsString = "111";
            poClock.DoClock(4.0);
            if (oReg.OutputNodes.ToString() != "111")
                return "fail 111";

            reset.NodeVectorAsString = "0";
            nvDataIn.NodeVectorAsString = "100";
            poClock.DoClock(4.0);
            if (oReg.OutputNodes.ToString() != "100")
                return "fail 100";

            //oReg.DebugDump(0);
            //poClock.DoClock(1);
            
            //System.Console.Clear();
            poTestComponent.ParentComponent.DebugDump(0);
            /*reset.NodeVectorAsString = "1";
            nvDataIn.NodeVectorAsString = "111";
            poClock.DoClock(1);*/

            return "";
            
        }

    }
}
